package com.clear.honghefinance.biz.modules.system.menu.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.clear.honghefinance.biz.constant.RedisKeyConstant;
import com.clear.honghefinance.biz.modules.system.menu.converter.SysMenuConverter;
import com.clear.honghefinance.biz.modules.system.menu.domain.entity.SysMenu;
import com.clear.honghefinance.biz.modules.system.member.domain.vo.AdminDTO;
import com.clear.honghefinance.biz.modules.system.menu.domain.dto.CreateMenuDTO;
import com.clear.honghefinance.biz.modules.system.menu.domain.dto.DeleteMenuDTO;
import com.clear.honghefinance.biz.modules.system.menu.domain.dto.UpdateMenuDTO;
import com.clear.honghefinance.biz.modules.system.menu.domain.vo.ListTreeMenuVO;
import com.clear.honghefinance.biz.modules.system.menu.domain.vo.ListTreeSelectMenuVO;
import com.clear.honghefinance.biz.modules.system.menu.mapper.SysMenuMapper;
import com.clear.honghefinance.biz.modules.system.menu.service.ISysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.clear.honghefinance.common.exception.BizException;
import com.clear.honghefinance.common.service.TokenService;
import com.clear.honghefinance.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统菜单 服务实现类
 * </p>
 *
 * @author 程序员clear
 * @since 2024-08-15
 */
@Service
@Slf4j
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Resource
    private TokenService<AdminDTO> tokenService;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 创建菜单
     *
     * @param dto req
     * @return 成功true、失败false
     */
    @Override
    public boolean create(CreateMenuDTO dto) {
        if (log.isInfoEnabled()) {
            log.info("创建菜单中，入参：{}", JSON.toJSONString(dto));
        }
        RLock rLock = redissonClient.getLock(RedisKeyConstant.CHANGE_MENU_LOCK);
        try {
            rLock.lock();
            //检测父级菜单
            int parentLevel = checkParent(dto.getPid());
            //更新菜单顺序
            updateSort(dto.getPid(), dto.getSort());

            SysMenu sysMenu = SysMenuConverter.INSTANCE.convertDTOToSysMenuEntity(dto);
            sysMenu.initDefault();
            sysMenu.setNodePath(parentLevel + 1);   // 在父级菜单下新增一个节点
            sysMenu.setMemberId(tokenService.getThreadLocalUserId());  // 用户id
            sysMenu.setUpdateMemberId(tokenService.getThreadLocalUserId());  // 修改用户id

            return this.save(sysMenu);
        } catch (Exception e) {
            throw new BizException(e.getMessage(), e);
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 修改菜单
     *
     * @param dto req
     * @return 成功true、失败false
     */
    @Override
    public boolean update(UpdateMenuDTO dto) {
        if (log.isInfoEnabled()) {
            log.info("修改菜单中，入参：{}", JSON.toJSONString(dto));
        }
        SysMenu sysMenu = getMenuById(dto.getId());
        if (Objects.isNull(sysMenu)) {
            throw new BizException("菜单不存在");
        }
        RLock rLock = redissonClient.getLock(RedisKeyConstant.CHANGE_MENU_LOCK);
        try {
            rLock.lock();
            //更新菜单顺序
            updateSort(sysMenu.getPid(), dto.getSort());

            SysMenu menu = SysMenuConverter.INSTANCE.convertDTOToSysMenuEntity(dto);
            LambdaUpdateWrapper<SysMenu> updateWrapper = Wrappers.<SysMenu>lambdaUpdate().eq(SysMenu::getId, dto.getId())
                    .eq(SysMenu::getDelFlag, false);
            return this.update(menu, updateWrapper);
        } catch (Exception e) {
            throw new BizException(e.getMessage(), e);
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 删除菜单
     *
     * @param dto req
     * @return 成功true、失败false
     */
    @Override
    public boolean delete(DeleteMenuDTO dto) {
        if (log.isInfoEnabled()) {
            log.info("删除菜单中，入参：{}", JSON.toJSONString(dto));
        }
        // 1. 检测子菜单，存在子菜单不允许删除
        int count = countChildMenuByPid(dto.getId());   // 传入的如果是父id，count>0
        if (count > 0) {
            throw new BizException("该菜单下存在子菜单，不允许删除");
        }
        // 2. 检测菜单是否存在
        SysMenu sysMenu = getMenuById(dto.getId());
        if (Objects.isNull(sysMenu)) {
            throw new BizException("菜单不存在");
        }
        // 3.逻辑删除菜单
        RLock rLock = redissonClient.getLock(RedisKeyConstant.CHANGE_MENU_LOCK);
        try {
            rLock.lock();
            LambdaUpdateWrapper<SysMenu> updateWrapper = Wrappers.<SysMenu>lambdaUpdate()
                    .eq(SysMenu::getId, dto.getId())
                    .set(SysMenu::getDelFlag, true)
                    .set(SysMenu::getUpdateMemberId, tokenService.getThreadLocalUserId())
                    .set(SysMenu::getUpdateTime, DateUtil.getSystemTime());
            return this.update(updateWrapper);
        } catch (Exception e) {
            throw new BizException(e.getMessage(), e);
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 获取菜单列表 树形展示
     *
     * @param name 菜单名称
     * @return
     */
    @Override
    public List<ListTreeMenuVO> listTreeMenu(String name) {
        if (log.isInfoEnabled()) {
            log.info("获取菜单列表（树形展示）中，入参：{}", JSON.toJSONString(name));
        }
        try {
            if (StringUtils.isNotBlank((name))) {
                // 1.like查询菜单
                // fixme 发现模糊查询后，菜单列表失去树形结构
                List<SysMenu> sysMenuList = getListByName(name);
                return SysMenuConverter.INSTANCE.convertSysMenuEntityListToListTreeMenuVOList(sysMenuList);
            } else {
                // 1.查询所有菜单，并以树形结构返回
                return getListTreeMenuByMenuIds();
            }
        } catch (Exception e) {
            throw new BizException("获取菜单列表异常", e);
        }
    }

    /**
     * 树形选择菜单列表
     *
     * @return
     */
    @Override
    public List<ListTreeSelectMenuVO> listTreeSelectMenu() {
        List<ListTreeSelectMenuVO> result = new ArrayList<>();

        try {
            // 1.创建根菜单
            ListTreeSelectMenuVO rootMenu = new ListTreeSelectMenuVO();
            rootMenu.setId(0);
            rootMenu.setValue("0");
            rootMenu.setTitle("根菜单");
            result.add(rootMenu);

            // 2.获取所有菜单
            List<SysMenu> list = getList();
            if (CollectionUtils.isEmpty(list)) {
                return result;
            }
            // 3.获取所有父级菜单 pid==0 即为父级菜单
            List<SysMenu> parentMenuList = list.stream().filter(p -> p.getPid() == 0).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(parentMenuList)) {
                return result;
            }
            // 4.将子菜单转换为ListTreeMenuVO。并组装到当前父级菜单的children中
            rootMenu.setChildren(SysMenuConverter.INSTANCE.convertSysMenuEntityListToListTreeSelectMenuVOList(parentMenuList));
            // 5.遍历根菜单的子菜单，为每个子菜单找到其子菜单列表
            for (ListTreeSelectMenuVO vo : rootMenu.getChildren()) {
                List<SysMenu> childMenuList = list.stream().filter(p -> Objects.equals(p.getPid(), vo.getId()))
                        .collect(Collectors.toList());
                vo.setChildren(SysMenuConverter.INSTANCE.convertSysMenuEntityListToListTreeSelectMenuVOList(childMenuList));
            }
            // 6.返回树形结构菜单列表
            return result;
        } catch (Exception e) {
            throw new BizException("获取菜单列表异常", e);
        }
    }


    /**
     * 获取树形菜单
     * 首先获取所有的菜单列表，根据pid进行分组，然后组装成树形结构
     *
     * @return
     */
    private List<ListTreeMenuVO> getListTreeMenuByMenuIds() {
        List<ListTreeMenuVO> result = null;
        // 1.查询所有菜单
        List<SysMenu> sysMenuList = getList();
        if (CollectionUtils.isEmpty(sysMenuList)) {
            return Collections.emptyList();
        }
        // 2.获取所有父级菜单 pid==0 即为父级菜单
        List<SysMenu> parentMenuList = sysMenuList.stream().filter(p -> p.getPid() == 0).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(parentMenuList)) {
            return Collections.emptyList();
        }
        // 3.将父级菜单转换为ListTreeMenuVO
        result = SysMenuConverter.INSTANCE.convertSysMenuEntityListToListTreeMenuVOList(parentMenuList);
        // 4.遍历父级菜单，为他们添加子菜单
        for (ListTreeMenuVO vo : result) {
            // a.筛选出当前父级菜单的所有子菜单
            List<SysMenu> childMenuList = sysMenuList.stream()
                    .filter(p -> Objects.equals(p.getPid(), vo.getId()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(childMenuList)) {
                continue;
            }
            // b.将子菜单转换为ListTreeMenuVO。并组装到当前父级菜单的children中
            vo.setChildren(SysMenuConverter.INSTANCE.convertSysMenuEntityListToListTreeMenuVOList(childMenuList));
        }
        // 5.返回树形结构菜单列表
        return result;
    }


    /**
     * 查询菜单列表
     *
     * @return
     */
    private List<SysMenu> getList() {
        LambdaQueryWrapper<SysMenu> lambdaQueryWrapper = Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getDelFlag, false)
                .eq(SysMenu::getDisable, false)
                .orderByAsc(SysMenu::getSort);
        return this.list(lambdaQueryWrapper);
    }

    /**
     * 查询菜单列表
     *
     * @return
     */
    private List<SysMenu> getListByName(String name) {
        LambdaQueryWrapper<SysMenu> lambdaQueryWrapper = Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getDelFlag, false)
                .eq(SysMenu::getDisable, false)
                .like(SysMenu::getName, name)   // 模糊查询菜单名称
                .orderByAsc(SysMenu::getSort);
        return this.list(lambdaQueryWrapper);
    }

    /**
     * 统计子菜单数量
     *
     * @param pid 菜单pid
     * @return 子菜单数量
     */
    private int countChildMenuByPid(int pid) {
        return this.count(Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getPid, pid)
                .eq(SysMenu::getDelFlag, false)
                .eq(SysMenu::getDisable, false)
        );
    }

    /**
     * 更新菜单顺序
     *
     * @param pid  需要更新排序的pid
     * @param sort
     * @return
     */
    private void updateSort(Integer pid, Integer sort) {
        this.update(Wrappers.<SysMenu>lambdaUpdate()
                .eq(SysMenu::getPid, pid)
                .eq(SysMenu::getDelFlag, false)
                .eq(SysMenu::getDisable, false)
                .eq(SysMenu::getSort, sort)
                .set(SysMenu::getSort, 1)
        );
    }

    /**
     * 检测父级菜单 并返回level级别
     *
     * @param pid pid
     * @return 节点深度 level 0表示一级节点
     * @throws BizException 不存在则抛出异常
     */
    private int checkParent(Integer pid) {
        if (pid == 0) {
            return 0;
        }
        SysMenu sysMenu = getMenuById(pid);
        if (Objects.isNull(sysMenu)) {
            throw new BizException("父级菜单不存在");
        }
        if (sysMenu.getPid() > 0) {
            throw new BizException("不可以创建三级菜单");
        }
        return sysMenu.getNodePath();
    }

    /**
     * 根据id获取菜单
     *
     * @param id 菜单id
     * @return 菜单
     */
    @Override
    public SysMenu getMenuById(int id) {
        return this.getOne(Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getId, id)
                .eq(SysMenu::getDelFlag, false)
                .eq(SysMenu::getDisable, false)
        );
    }

    /**
     * 根据id查询菜单列表
     *
     * @param menuIds 菜单id列表
     * @return
     */
    @Override
    public List<SysMenu> listMenuByIds(List<Integer> menuIds) {
        if (CollectionUtils.isEmpty(menuIds)) {
            return Collections.emptyList();
        }
        return this.list(Wrappers.<SysMenu>lambdaQuery()
                .in(SysMenu::getId, menuIds)
                .eq(SysMenu::getDelFlag, false)
        );
    }

    /**
     * 获取所有菜单列表
     *
     * @return
     */
    @Override
    public List<SysMenu> listMenu() {
        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getDelFlag, false)
                .eq(SysMenu::getDisable, false)
                .orderByAsc(SysMenu::getSort);
        return this.list(queryWrapper);
    }


}
